(function() {

function isDescendant(el, parent) {
	// Firefox sometimes assigns relatedTarget a XUL element
	// which we cannot access the parentNode property of
	// credit: jQuery
	try {
		while ((el = el.parentNode)) {
			if (el == parent) {
				return true;
			}
		}
	}
	catch (e) {}
	return false;
}
function wrapMouseEnterLeave(type, handler, el) {
	var fn = function(event) {
		if (event.relatedTarget != el && !isDescendant(event.relatedTarget, el)) {
			handler.call(el, event);
		}
	};
	handler['_handler' + type] = fn;
	return fn;
}
function unwrapMouseEnterLeave(type, handler) {
	return (handler['_handler' + type] || handler);
}

Element.addMethods({
	observe: function observe(type, handler) {
		if (type == 'dom:loaded') {
			type = 'DOMContentLoaded';
		}
		else if (
			(type == 'mouseenter' || type == 'mouseleave')
			&& !supportsMEL
		) {
			handler = wrapMouseEnterLeave(type, handler, this);
			type = (type == 'mouseenter' ? 'mouseover' : 'mouseout');
		}
		else if (type == ':hover') {
			return this.observe('mouseenter', handler)
				.observe('mouseleave', arguments[2]);
		}
		this.addEventListener(type, handler, false);
		return this;
	},
	stopObserving: function stopObserving(type, handler) {
		if (type == 'dom:loaded') {
			type = 'DOMContentLoaded';
		}
		else if (
			(type == 'mouseenter' || type == 'mouseleave')
			&& !supportsMEL
		) {
			handler = unwrapMouseEnterLeave(type, handler);
			type = (type == 'mouseenter' ? 'mouseover' : 'mouseout');
		}
		else if (type == ':hover') {
			return this.stopObserving('mouseenter', handler)
				.stopObserving('mouseleave', arguments[2]);
		}
		this.removeEventListener(type, handler, false);
		return this;
	}
});
})();